; (function (window) {

  function getDom(selector) {
    return document.querySelector(selector)
  }

  function getDoms(selector) {
    return document.querySelectorAll(selector)
  }

  function getStyle(dom, attr) {
    return window.getComputedStyle(dom)[attr]
  }

  function setStyle(dom, attr, value) {
    dom.style[attr] = value
  }

  function addEvent(dom, type, fn) {
    dom.addEventListener(type, fn)
  }

  function removeEvent(dom, type, fn) {
    dom.removeEventListener(type, fn)
  }

  function addClass(dom, className) {
    dom.classList.add(className)
  }

  function removeClass(dom, className) {
    dom.classList.remove(className)
  }

  function hasClass(dom, className) {
    return dom.classList.contains(className)
  }

  function ready(fn) {
    if (document.readyState !== 'loading') {
      fn()
    } else {
      document.addEventListener('DOMContentLoaded', fn)
    }
  }


  /**
   * 它执行 AJAX 请求，提供可自定义的选项以及成功和错误处理的回调。
   * @param options - `options` 参数是一个包含 AJAX 请求配置的对象。它可以具有以下属性：
   * @returns XMLHttpRequest 对象。
   */
  function ajax(options) {
    var defaultOptions = {
      url: '',
      method: 'GET',
      data: null,
      async: true,
      success: null,
      error: null
    },
      xhr = new XMLHttpRequest(),
      options = Object.assign({}, defaultOptions, options)

    xhr.open(options.method, options.url, options.async)

    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          options.success && options.success(xhr.responseText, xhr.responseXML)
        } else {
          options.error && options.error(xhr.status)
        }
      }
    }

    xhr.send(options.data)

    return xhr
  }

  /**
   * 函数“isInViewPortOfOne”检查元素是否在视口中，如果是则执行回调函数。
   * @param el - “el”参数是您要检查是否在视口中的元素。它可以是任何有效的 DOM 元素。
   * @param callback - 回调是当元素位于视口中时将执行的函数。
   */
  function isInViewPortOfOne(element, callback, noisback) {
    // 判断是否支持 observer
    if (window.IntersectionObserver) {
      const observer = new IntersectionObserver((entry) => {
        if (entry[0].isIntersecting) {
          callback()
        } else {
          if (noisback) {
            noisback()
          }
        }
      }, {
        root: null,
        threshold: 0.5
      });
      observer.observe(element)
    } else {
      // 监听 scroll 事件
      window.addEventListener('scroll', () => {
        const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
        const scrollTop = document.documentElement.scrollTop;
        const eleOffsetTop = element.offsetTop;
        if (scrollTop > eleOffsetTop - viewportHeight) {
          callback()
        }
      })
    }
  }


  class NumberScroll {
    constructor(options) {
      const defaultOptions = {
        speed: '2s', // 滚动速度, 每多少秒滚动一次
        delay: 0, // 延迟多少秒开始
        init: true, // 是否初始化
        startNumber: 0, // 开始的数字
        callback: null // 回调函数
      }
      this.options = Object.assign({}, defaultOptions, options)
      this.init()
    }

    init() {
      this.$el = this.options.el || this.options.element
      this.$el.style.transition = `all ${this.options.speed} ease-in-out`

      this.interval = null

      this.endNumber = parseInt(this.$el.textContent) || 0 // 结束的数字
      this.startNumber = this.options.startNumber || 0 // 开始的数字
      this.currentNumber = 0 // 当前的数字

      if (this.options.init && this.$el) {
        this.start()
      }
    }

    start() {
      const startTime = Date.now() // 开始的时间
      const duration = this.options.speed.replace('s', '') * 1000 // 数字滚动持续的时间
      const delay = this.options.delay * 1000 // 延迟几秒开始滚动,注意延迟期间数字不会变化

      const endNumber = this.endNumber // 结束的数字
      const startNumber = this.startNumber // 开始的数字

      const callback = this.options.callback // 回调函数

      // 延时检测
      if (delay) {
        setTimeout(() => {
          this.interval = setInterval(() => {
            this.animateDelay(startTime, delay, duration, endNumber, startNumber, callback)
          }, 1000 / 60)
        }, delay)
      } else {
        this.interval = setInterval(() => {
          this.animateDelay(startTime, delay, duration, endNumber, startNumber, callback)
        }, 1000 / 60)
      }
    }

    animateDelay(startTime, delay, duration, endNumber, startNumber, callback) {
      const time = Date.now() - startTime - delay //  获取当前时间并计算出时间差值
      const finish = this.$el.getAttribute('data-finish') // 获取自定义属性 data-finish
      if (finish === 'true') {
        return
      }
      // 如果时间超过了持续时间,则停止滚动
      if (time >= duration) {
        this.stop() // 停止滚动
        this.$el.textContent = endNumber // 设置最终的数字
        if (callback) {
          callback() // 执行回调函数

        }
        this.$el.setAttribute('data-finish', 'true')
        return
      }
      // 计算当前的数字
      this.currentNumber = Math.floor((endNumber - startNumber) * (time / duration) + startNumber)
      this.$el.textContent = this.currentNumber

      if (callback) {
        callback()
      }
      // 添加自定义属性 data-finsh
      this.$el.setAttribute('data-finish', 'false')
    }

    stop() {
      clearInterval(this.interval)
    }
  }


  window.utils = {
    getDom,
    getDoms,
    getStyle,
    setStyle,
    addEvent,
    removeEvent,
    addClass,
    removeClass,
    hasClass,
    ready,
    ajax,
    isInViewPortOfOne,
    NumberScroll
  }
})(window)
